{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## QGAN\n",
    "\n",
    "In this notebook, we are going to use Aqua's implementation of a Quantum Generative Adversarial Network to prepare a quantum state which is close to a given probability distribution "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from qiskit import Aer\n",
    "from qiskit.aqua import QuantumInstance\n",
    "from qiskit.aqua.algorithms import QGAN"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We first generate training data taken from a simple probability distribution"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.random.seed(2020)\n",
    "N = 1000\n",
    "\n",
    "real_data = np.random.binomial(3,0.5,N)\n",
    "plt.hist(real_data, bins = 4);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we define the parameters for our QAN and train it with the real data. Then, we use the trained generator to generate some samples and we represent them in a histogram.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "n = 2\n",
    "num_qubits = [n]\n",
    "num_epochs = 100\n",
    "batch_size = 100\n",
    "bounds = [0,3]\n",
    "qgan = QGAN(data = real_data, \n",
    "            num_qubits = num_qubits, \n",
    "            batch_size = batch_size, \n",
    "            num_epochs = num_epochs,\n",
    "            bounds = bounds,\n",
    "            seed = 2020)\n",
    "quantum_instance = QuantumInstance(backend=Aer.get_backend('statevector_simulator'))\n",
    "result = qgan.run(quantum_instance)\n",
    "samples_g, prob_g = qgan.generator.get_output(qgan.quantum_instance, shots=10000)\n",
    "plt.hist(range(4), weights = prob_g, bins = 4);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We show the evolution of the loss function of both the generator and the discriminator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.title(\"Loss function evolution\")\n",
    "plt.plot(range(num_epochs), qgan.g_loss, label='Generator')\n",
    "plt.plot(range(num_epochs), qgan.d_loss, label='Discriminator')\n",
    "plt.legend()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And, also, the evolution of the relative entropy during the training."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.title('Relative entropy evolution')\n",
    "plt.plot(qgan.rel_entr)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import qiskit\n",
    "qiskit.__qiskit_version__"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
